package com.jack.concurrentdb;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;

/**
 * @Author jack chan
 * @Description //测试并发请求执行sql的问题。
 *                悲观锁和乐观锁的应用
 * @Date 11:59 下午 2020/8/8
 **/
public class ConcurrentTest {
    //SqlSession工厂
    static SqlSessionFactory sqlSessionFactory;
    //统计成功🌲
    static int countUpdated = 0;
    //统计不成功🌲
    static int countUnUpdated = 0;

    static {
        String config = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(config);
        } catch (IOException e) {
            e.printStackTrace();
        }
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

    }

    final static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");


    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(1);//模拟5人并发请求，用户钱包


        for (int i = 0; i < 50; i++) {//模拟5个用户
            AnalogUser analogUser = new AnalogUser("user" + i, "58899dcd-46b0-4b16-82df-bdfd0d953bfb", "1", "5", latch);
            analogUser.start();
        }
        for (int i = 0; i < 50; i++) {
            AnalogUser analogUser = new AnalogUser("user" + i, "58899dcd-46b0-4b16-82df-bdfd0d953bfb", "2", "5", latch);
            analogUser.start();
        }

        System.out.println("所有模拟并发请求  at " + sdf.format(new Date()));
        latch.countDown();
        //计数器減一  所有线程释放 并发访问。

    }

    static class AnalogUser extends Thread {
        String workerName;//模拟用户姓名
        String openId;
        String openType;
        String amount;
        CountDownLatch latch;

        public AnalogUser(String workerName, String openId, String openType, String amount,
                          CountDownLatch latch) {
            this.workerName = workerName;
            this.openId = openId;
            this.openType = openType;
            this.amount = amount;
            this.latch = latch;
        }

        @Override
        public void run() {
            try {
                latch.await();  //等待开始；
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            post();

        }

        public void post() {

            System.out.println("模拟用户： " + workerName + " 开始发送模拟请求  at " + sdf.format(new Date()));
            try (SqlSession sqlSession = sqlSessionFactory.openSession()) {

                AccountWallet wallet = sqlSession.selectOne("com.jack.concurrentdb.AccountWalletDao.selectByOpenId", openId);

                System.err.println(wallet.getUserAmount());
                // 用户操作金额
                BigDecimal cash = BigDecimal.valueOf(Double.parseDouble(amount));
                if (Integer.parseInt(openType) == 1) {
                    wallet.setUserAmount(wallet.getUserAmount().add(cash));
                } else if (Integer.parseInt(openType) == 2) {
                    wallet.setUserAmount(wallet.getUserAmount().subtract(cash));
                }

                int update = sqlSession.update("com.jack.concurrentdb.AccountWalletDao.updateAccountWallet", wallet);

                sqlSession.commit();

                synchronized (ConcurrentTest.class) {
                    if (update == 0) {
                        countUnUpdated += 1;
                        System.out.println("更新失败，被抛弃请求🌲:" + countUnUpdated);
                    }
                    //计算成功更新
                    countUpdated += update;
                    System.out.println("成功更新数据有:" + countUpdated + " 条");
                }

                System.out.println("模拟用户： " + workerName + " 模拟请求结束  at " + sdf.format(new Date()));

            }

        }


    }

}